package com.game.utils;

import com.game.config.Coefficient;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
//数据库操作工具类 懒写 copy from：https://blog.csdn.net/pengzonglu7292/article/details/86625415
public abstract class JDBCUtil {
    /*
     * jdbc变量
     */
    static String driver=null;
    static String url=null;
    static String user=null;
    static String password=null;
    //thread封装Connection对象
    static ThreadLocal<Connection> thread=new ThreadLocal<Connection>();
    //唯一空构造器私有化，不允许外部创建对象
    private JDBCUtil() {}
    /**
     *  静态代码块为jdbc变量赋值
     * 因为静态代码块最先执行，所以调用getConnection()方法时，
     * 该方法内部的jdbc变量就完成了赋值操作
     */
    static {
        try {
            /*
             * Constants常量接口中保存了很多常量，这些常量的值就是配置文件k-v数据的键
             *
             */
            driver=Coefficient.JDBC_DRIVER;
            url=Coefficient.JDBC_URL;
            user=Coefficient.JDBC_USER;
            password=Coefficient.JDBC_PASSWORD;
            /*
             * 加载驱动，静态代码块只执行一次，驱动只加载一次（加载驱动很耗性能的）
             */
            Class.forName(driver);//加载驱动
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 通过本方法获取一个MySQL数据库的Connection对象
     *
     * @return Connection对象
     */
    public static Connection getConnection() {
        Connection con = thread.get();
        if(con==null) {
            try {
                con = DriverManager.getConnection(url, user, password);
                thread.set(con);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return con;//返回jdbc连接
    }

    /**
     * 本方法中调用Date类型变量的setter方法时使用的是java.sql.Date，
     * 所以实体类在声明Date类型变量时一定声明成java.sql.Date
     * 至少Date类型变量对应的setter方法的形参必须是java.sql.Date，否则报错
     *
     * 查询完毕后，使用者通过JDBCHelper.getConnection()获取连接对象，并关闭它
     * 外部获取的连接对象与本方法使用的连接对象，在同一线程类，是同一个对象
     *
     * @param sql   要执行的查询语句
     * @param t      实体类对象
     * @param objs    SQL语句中的参数
     * @return       装有实体类对象的list集合
     */
//    public static <T> List<T>  executeQuery1(String sql,T t,Object...objs){
//        //声明jdbc变量
//        List<T> list=new ArrayList<>();
//        Connection conn = null;
//        PreparedStatement ps =null;
//        ResultSet rs =null;
//        try {
//            conn = JDBCUtil.getConnection();
//            ps = conn.prepareStatement(sql);
//            //给占位符赋值
//            if(objs!=null) {
//                for(int i=0;i<objs.length;i++) {
//                    ps.setObject((i+1), objs[i]);
//                }
//            }
//            //执行sql语句
//            rs = ps.executeQuery();
//            //获取结果集中字段的所有信息
//            ResultSetMetaData rm = rs.getMetaData();
//            int columnCount = rm.getColumnCount();//获取字段数
//            //遍历结果集
//            while(rs.next()) {
//                Class<? extends Object> cla = t.getClass();//获取类对象
//                T newInstance=(T)cla.newInstance();//获取类的对象
//                //一个for循环封装一条记录的所有值
//                for(int i=1;i<=columnCount;i++) {
//                    String columnName = rm.getColumnName(i);//获取字段名
//                    //获取字段对应的setter方法
//                    String methodName="set"+columnName.substring(0, 1).toUpperCase()+columnName.substring(1);
//                    String columnClassName = rm.getColumnClassName(i);//获取字段java类型的完全限定名
//                    //创建方法对象
//                    Method method = cla.getDeclaredMethod(methodName, Class.forName(columnClassName));
//                    method.invoke(newInstance,rs.getObject(columnName));//调用setter方法，执行对象属性赋值
//                }
//                list.add(newInstance);//将对象加入集合
//            }
//        } catch (Exception  e) {
//            e.printStackTrace();
//        }finally {
//            //关流
//            JDBCUtil.close(ps,rs);
//        }
//        return list;
//    }

    public static ResultSet  executeQuery(String sql,Object...objs){
        //声明jdbc变量
        Connection conn = null;
        PreparedStatement ps =null;
        ResultSet rs =null;
        try {
            conn = JDBCUtil.getConnection();
            ps = conn.prepareStatement(sql);
            //给占位符赋值
            if (objs != null) {
                for (int i = 0; i < objs.length; i++) {
                    ps.setObject((i + 1), objs[i]);
                }
            }
            //执行sql语句
            rs = ps.executeQuery();
        }catch (Exception e){
            e.printStackTrace();
        }
        return rs;
    }
    /**
     * 该方法封装了MySQL数据库的DML操作
     * 如果要实现事务：
     * 		事务的开启，关闭，回滚，及连接对象的关闭等操作
     * 			使用者通过JDBCHelper.getConnection()获取连接对象，通过连接对象并在外部声明
     * 			外部获取的连接对象与本方法使用的连接对象，在同一线程类，是同一个对象
     *
     * @param sql   要执行的SQL语句
     * @param objs  SQL语句中的参数
     * @return   成功执行返回影响的记录条数，否则返回0
     * @throws SQLException
     */
    public static Integer executeDML(String sql,Object...objs) {
        //声明jdbc变量
        Connection conn =null;
        PreparedStatement ps =null;
        Integer i =0;
        try{
            conn = JDBCUtil.getConnection();
            ps = conn.prepareStatement(sql);
            //给占位符赋值
            if(objs!=null) {
                for(int j=0;j<objs.length;j++) {
                    ps.setObject((j+1), objs[j]);
                }
            }
            //执行SQL语句
            i = ps.executeUpdate();
        }catch(SQLException e) {
            e.printStackTrace();
        }finally {
            //关流
            JDBCUtil.close(ps);
        }
        return i;
    }
    /**
     * 关流的方法，接收任意多个任意类型的流对象
     * 如果关闭的流对象有关闭的先后顺序
     * 请将要先关闭的流对象放在前方
     *
     * 所有流对象的顶级父接口都是AutoCloseable
     * @param t    要关闭的流对象，可以是一个或多个（也可以是零个）
     *
     */
    public static <T>void close(T...t){
        //循环关流
        for(T tmp:t) {
            //关闭流对象
            if(tmp instanceof AutoCloseable) {
                try {
                    ((AutoCloseable)tmp).close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}